10.6 代理机制

Spring AOP使用JDK动态代理机制或者CGLIB代理机制,为目标对象创建动态代理。(默认使用JDK动态代理机制)
如果被代理的目标对象实现了至少一个接口,那么将使用JDK动态代理机制。 目标类型所有实现的接口都将被代理。
如果目标对象没有实现任何接口,那么将使用CGLIB代理机制。
如果你想强制使用CGLIB代理机制(例如你需要对目标对象的每一个方法进行代理而不是对他实现的接口。), 你可以这样做.但是,这里有一些问题你必须要考虑: - final方法不能够被通知,就像他们不能够被重写。
- Spring3.2版本后,你不需要添加CGLIB到你的项目的classpath下,org.springframework将CGLIB重新打包封装到了spring-core JAR中。 这意味着基于CGLIB的代理的支持,就像JDK动态代理运用一样。
- 自从Spring 4.0凭借Objenesis创建CGLIB代理示例以后,你代理的对象的构造器不在会被调用2次。 仅仅在你的JVM不允许绕过构造器时,你才可能会看到,代理对象的构造器被调用2次,以及2次调用的相关日志。

强制使用CGLIB代理,设proxy-target-class属性为true:

<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>

当使用 @AspectJ 自动代理支持时强制使用CGLIB代理,请设置 proxy-target-class属性值为true:

<aop:aspectj-autoproxy proxy-target-class="true"/>

[注意]

多个 片段在运行时会聚合到一个统一的自动代理建造者中,该建造者应用所有中最强的代理设置。 这也适用于 两个元素。

要明确的是:设置proxy-target-class="true" 会强制 , 或者 元素都是用CGLIB代理。

10.6.1理解AOP代理

Spring AOP是基于代理的。在你写自己的切面或者使用Spring框架提供的Spring AOP切面之前,代理机制是你需要理解掌握的极其重要的一个概念。

考虑第一个场景,你有一个单纯功能,未代理,没有任何特别的,直接的对象引用,就像下面的代码片段展示的一样:

public class SimplePojo implements Pojo {

    public void foo() {
        // 以直接引用this的方方式调用,bar方法。
        this.bar();
    }

    public void bar() {
        // 一些业务逻辑
    }
}

如果您在对象引用上调用方法,则可以直接调用该方法,如下所示

public class Main {

    public static void main(String[] args) {

        Pojo pojo = new SimplePojo();

        //这是在‘pojo’对象引用上直接进行方法调用 
        pojo.foo();
    }
}

当使用代理时事情发生了略微的变化。如下图和代码片段所示:

public class Main {

    public static void main(String[] args) {

        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());

        Pojo pojo = (Pojo) factory.getProxy();

        // 这是在代理上调用方法
        pojo.foo();
    }
}

你需要理解的核心部分是,在Main类的main方法中的代码,对代理的引用部分。
这意味着对对象引用的方法调用将是代理上的调用,同样地代理将能够将所有的拦截器(通知)委托到相关的特定的方法调用上。
但是,一旦调用到达最终的目标对象,在本例中是SimplePojo引用时,任何方法调用都在最终目标对象上调用(也就是SimplePojo上) 这有很重要的影响。这意味着自调用不会通知相关的切面,切面上的业务方法也不会被执行。

那么,我们该怎么做(才能够将对应的方法通知到切面,并执行相关业务逻辑呢)? 最好的方法(保持松耦合)是重构你的代码,保证这种自调用不会出现。
确实,这样你必须要做一些工作,但是这是最好的,侵入性最小的方法。
另外一种的方法是非常可怕的,他是那么的可怕,以至于我几乎不愿意把它指出来。
您可以(窒息!)通过这样做,将您的类中的逻辑完全绑定到Spring AOP中。

public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

这将你的代码完全绑定到Spring AOP上,它让类使用AOP上下文自己调用切面。 这也需要在创建代理时做一些额外的配置,如下:

``` public class Main {

public static void main(String[] args) {

    ProxyFactory factory = new ProxyFactory(new SimplePojo());
    factory.adddInterface(Pojo.class);
    factory.addAdvice(new RetryAdvice());
    //设置暴露代理为true
    factory.setExposeProxy(true);

    Pojo pojo = (Pojo) factory.getProxy();

    // this is a method call on the proxy!
    pojo.foo();
}

} ```

最后,必须注意AspectJ没有自调用问题,因为它不是基于代理的AOP框架。